﻿// =====================================================================
//
//  This file is part of the Microsoft Dynamics CRM SDK code samples.
//
//  Copyright (C) Microsoft Corporation.  All rights reserved.
//
//  This source code is intended only as a supplement to Microsoft
//  Development Tools and/or on-line documentation.  See these other
//  materials for detailed information regarding Microsoft code samples.
//
//  THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//  PARTICULAR PURPOSE.
//
// =====================================================================

using System;
using System.Collections.Generic;
using SL.IdentityModel.Protocols.WSTrust;

namespace SL.IdentityModel.Services
{
	/// <summary>
	/// Class caches tokens, bases on appliesTo, to be used in issuedToken calls
	/// </summary>
	public class TokenCache
	{
		private TimeSpan _allowedTimeSkew = new TimeSpan(0, 5, 0);
		private object _lock = new object();
		private Dictionary<string, RequestSecurityTokenResponse> _tokenCache = new Dictionary<string, RequestSecurityTokenResponse>();
		private bool _cacheEnabled = true;

		/// <summary>
		/// Allows disabling of the cache, if a new token should be used on each call
		/// </summary>
		public bool CacheEnabled
		{
			get { return _cacheEnabled; }
			set { _cacheEnabled = value; }
		}

		/// <summary>
		/// inserts a token into the cache
		/// </summary>
		/// <param name="appliesTo">appliesTo identifies the target service</param>
		/// <param name="rstr">Token response containing security token</param>
		public void AddTokenToCache(string appliesTo, RequestSecurityTokenResponse rstr)
		{
			lock (_lock)
			{
				if (_cacheEnabled)
				{
					if (HasTokenInCache(appliesTo))
					{
						_tokenCache.Remove(appliesTo);
					}

					_tokenCache.Add(appliesTo, rstr);
				}
			}
		}

		/// <summary>
		/// looks up a token base on the appliesTo provided
		/// </summary>
		/// <param name="appliesTo">appliesTo to identify the target service</param>
		/// <returns>RSTR, or null if no valid token is in the cache</returns>
		public RequestSecurityTokenResponse GetTokenFromCache(string appliesTo)
		{
			RequestSecurityTokenResponse token = null;

			lock (_lock)
			{
				if (HasTokenInCache(appliesTo) && _cacheEnabled == true)
				{
					token = _tokenCache[appliesTo];
				}
			}

			return token;
		}

		/// <summary>
		/// checks if an unexpired token is in the cache for the given appliesTO
		/// </summary>
		/// <param name="appliesTo">identifier for the target service</param>
		/// <returns>true if valid token is in cache, else false</returns>
		public bool HasTokenInCache(string appliesTo)
		{
			bool hasValidToken = false;

			lock (_lock)
			{
				if (_tokenCache.ContainsKey(appliesTo))
				{
					RequestSecurityTokenResponse rstr = _tokenCache[appliesTo];

					if ((rstr.Created < DateTime.Now.Add(_allowedTimeSkew) && (rstr.Expires > DateTime.Now.Add(_allowedTimeSkew))))
					{
						hasValidToken = true;
					}
					else
					{
						_tokenCache.Remove(appliesTo);
					}
				}
			}
			return hasValidToken;
		}
	}
}
